from app.db import db  # 从独立的 db 模块导入
from app.Models.FinanceModels.ExpenseSecondCate import ExpenseSecondCate  # 按实际路径调整
from sqlalchemy.exc import IntegrityError


class ExpenseSecondCateDAO:
    """支出二级分类模型的数据访问对象"""

    @staticmethod
    def create_second_cate(second_cate_name, first_cate_id):
        """
        新增支出二级分类
        :param second_cate_name: 二级分类名称（如"早餐"、"午餐"）
        :param first_cate_id: 所属一级分类ID
        :return: 新增的二级分类对象
        :raises ValueError: 名称为空、一级分类不存在或名称重复时抛出
        """
        # 1. 基础校验：名称非空
        if not second_cate_name or second_cate_name.strip() == "":
            raise ValueError("支出二级分类名称不能为空")

        # 2. 校验一级分类是否存在（依赖外部传入的一级分类DAO）
        # 注：实际使用时需先通过ExpenseFirstCateDAO验证first_cate_id有效性
        # 此处仅做关联逻辑，不直接依赖其他DAO以避免循环导入

        # 3. 校验同属一级分类下的二级分类名称是否重复
        existing = ExpenseSecondCateDAO.get_second_by_name_and_first(
            second_cate_name, first_cate_id
        )
        if existing:
            raise ValueError(f"该一级分类下已存在二级分类：{second_cate_name}")

        # 4. 执行新增
        try:
            new_cate = ExpenseSecondCate(
                second_cate_name=second_cate_name.strip(),
                first_cate_id=first_cate_id
            )
            db.session.add(new_cate)
            db.session.commit()
            return new_cate
        except IntegrityError:
            db.session.rollback()
            raise ValueError(f"创建失败：分类名称重复或一级分类不存在")

    @staticmethod
    def get_second_cate_by_id(cate_id):
        """
        根据ID查询二级分类
        :param cate_id: 二级分类ID
        :return: 二级分类对象或None
        """
        return ExpenseSecondCate.query.get(cate_id)

    @staticmethod
    def get_second_by_name_and_first(second_name, first_id):
        """
        按名称和一级分类ID查询二级分类（用于同级别名称去重）
        :param second_name: 二级分类名称
        :param first_id: 一级分类ID
        :return: 二级分类对象或None
        """
        if not (second_name and first_id):
            return None
        return ExpenseSecondCate.query.filter_by(
            second_cate_name=second_name.strip(),
            first_cate_id=first_id
        ).first()

    @staticmethod
    def get_seconds_by_first_cate(first_cate_id):
        """
        查询指定一级分类下的所有二级分类
        :param first_cate_id: 一级分类ID
        :return: 二级分类列表（空列表表示无数据）
        """
        return ExpenseSecondCate.query.filter_by(
            first_cate_id=first_cate_id
        ).order_by(ExpenseSecondCate.id.asc()).all()

    @staticmethod
    def get_all_second_cates():
        """
        查询所有二级分类（含一级分类关联信息）
        :return: 包含一级分类信息的二级分类列表
        """
        return ExpenseSecondCate.query.order_by(
            ExpenseSecondCate.first_cate_id.asc(),
            ExpenseSecondCate.id.asc()
        ).all()

    @staticmethod
    def update_second_cate(cate_id, new_name=None, new_first_id=None):
        """
        更新二级分类（支持修改名称和所属一级分类）
        :param cate_id: 二级分类ID
        :param new_name: 新名称（可选）
        :param new_first_id: 新一级分类ID（可选）
        :return: 更新后的二级分类对象
        :raises ValueError: 分类不存在、参数无效或名称重复时抛出
        """
        # 1. 校验分类是否存在
        cate = ExpenseSecondCateDAO.get_second_cate_by_id(cate_id)
        if not cate:
            raise ValueError(f"支出二级分类不存在（ID：{cate_id}）")

        # 2. 处理名称更新
        if new_name is not None:
            new_name = new_name.strip()
            if not new_name:
                raise ValueError("二级分类名称不能为空")

            # 校验新名称在目标一级分类下是否重复
            target_first_id = new_first_id or cate.first_cate_id
            existing = ExpenseSecondCateDAO.get_second_by_name_and_first(
                new_name, target_first_id
            )
            if existing and existing.id != cate_id:
                raise ValueError(f"目标分类下已存在名称：{new_name}")

            cate.second_cate_name = new_name

        # 3. 处理所属一级分类更新
        if new_first_id is not None and new_first_id != cate.first_cate_id:
            # 注：实际使用时需先通过ExpenseFirstCateDAO验证new_first_id有效性
            cate.first_cate_id = new_first_id

        # 4. 执行更新
        try:
            db.session.commit()
            return cate
        except IntegrityError:
            db.session.rollback()
            raise ValueError("更新失败：一级分类不存在或名称重复")

    @staticmethod
    def delete_second_cate(cate_id, check_usage=False, expense_dao=None):
        """
        删除二级分类
        :param cate_id: 二级分类ID
        :param check_usage: 是否校验该分类是否被支出记录引用
        :param expense_dao: 支出记录DAO（check_usage=True时必填）
        :return: True（删除成功）
        :raises ValueError: 分类不存在、被引用或删除失败时抛出
        """
        # 1. 校验分类是否存在
        cate = ExpenseSecondCateDAO.get_second_cate_by_id(cate_id)
        if not cate:
            raise ValueError(f"支出二级分类不存在（ID：{cate_id}）")

        # 2. 可选：校验是否被支出记录引用
        if check_usage:
            if not expense_dao:
                raise ValueError("校验使用情况时需传入expense_dao")
            # 调用支出DAO查询是否有引用该二级分类的记录
            related_expenses = expense_dao.get_expenses_by_second_cate(cate_id)
            if related_expenses:
                raise ValueError(
                    f"无法删除：该分类被{len(related_expenses)}条支出记录引用"
                )

        # 3. 执行删除
        try:
            db.session.delete(cate)
            db.session.commit()
            return True
        except Exception as e:
            db.session.rollback()
            raise ValueError(f"删除失败：{str(e)}")
